The brythonmagic extension has been tested on:

In [ ]:
import IPython
IPython.version_info

brythonmagic installation

Just type the following:

In [ ]:
%install_ext https://raw.github.com/kikocorreoso/brythonmagic/master/brythonmagic.py
In [ ]:
%load_ext brythonmagic

And load the brython js lib in the notebook:

In [ ]:
%%HTML
<script type="text/javascript" src="http://brython.info/src/brython_dist.js"></script>

Warning

In order to load javascript libraries in a safety way you should try to use https instead of http when possible (read more here). If you don't trust the source and/or the source cannot be loaded using https then you could download the javascript library and load it from a local location.

Usage:

The brythonmagic provides you a cell magic, %%brython, to run brython code and show the results in a html div tag below the code cell.

You can use several options:

  • -p, --print: will show you the generated html code below the results obtained from the brython code.
  • -c, --container: you can define de name of the div container in case you want to 'play' with it in other cell. If you don't define an output the div will have and id with the following format 'brython-container-[random number between 0 and 999999]'
  • -i, --input: you can pass variables defined in the Python namespace separated by commas. If you pass a python list it will be converted to a brython list, a python tuple will be converted to a brython tuple, a python dict will be converted to a brython dict, a python string will be converted to a brython string.
  • -h, --html: you can pass a string with html markup code. This html code will be inserted inside the div container. In this way you can avoid the generation of HTML markup code via a Brython script so you can separate the layout from the 'action'.
  • -s, --script: Use this option to provide and id to the script defined in the Brython code cell. Also, this value could be used to run the code of this cell in other brython cells.
  • -S, --scripts: Use this option to run code previously defined in other Brython code cells. The values should be the provided values in the -s/--script option in other Brython code cells.

  • -f, --fiddle: With this option, the code in the cell will be automatically uploaded to gist.github.com/ as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on jsfiddle.net. Finally, some links will be printed in the output linking to the gist and the fiddle. See an example here (https://gist.github.com/anonymous/b664e8b4617afc09db6c and http://jsfiddle.net/gh/gist/library/pure/b664e8b4617afc09db6c/)

  • -e, --embedfiddle: With this option, the code in the cell will be automatically uploaded to gist.github.com/ as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on jsfiddle.net. Finally, some links will be printed in the output linking to the gist and the fiddle and an iframe will be created showing the fiddle on jsfiddle.net.

[WARNING] This options may change as the brythonmagic is in active development.

-p, --print option

The following example shows the use of the -p, --print option.

[HINT] The result of the print is shown in the javascript console of your browser.

In [ ]:
%%brython -p
print('hello world!')

-c, --container option

In the following example can be seen the use of the -c, --container. The -p is also used to show you the result. See the id attribute of the div tag created:

In [ ]:
%%brython -c my_container -p
from browser import doc, html

# This will be printed in the js console of your browser
print('Hello world!')

# This will be printed in the container div on the output below
doc["my_container"] <= html.P("This text is inside the div", 
                              style = {"backgroundColor": "cyan"})

-i, --input option

In this example you can see how the data are passed to brython from python using the -i or --input option. First, we create some data in a regular Python cell.

In [ ]:
data_list = [1,2,3,4]

data_tuple = (1,2,3,4)

data_dict = {'one': 1, 'two': 2}

data_str = """
Hello
GoodBye
"""

# A numpy array can be converted to a list and you will obtain a brython list
import numpy as np
data_arr = np.empty((3,2))
data_arr = data_arr.tolist()

And now, the created data are passed to Brython and used in the Brython code cell. Remember that only Python lists, tuples, dicts and strings are allowed as inputs.

In [ ]:
%%brython -c p2b_data_example -i data_list data_tuple data_dict data_str data_arr
from browser import doc, html

doc["p2b_data_example"] <= html.P(str(data_list))
doc["p2b_data_example"] <= html.P(str(type(data_list)))

doc["p2b_data_example"] <= html.P(str(data_tuple))
doc["p2b_data_example"] <= html.P(str(type(data_tuple)))

doc["p2b_data_example"] <= html.P(str(data_dict))
doc["p2b_data_example"] <= html.P(str(type(data_dict)))

doc["p2b_data_example"] <= html.P(data_str.replace('Hello', 'Hi'))
doc["p2b_data_example"] <= html.P(str(type(data_str)))

doc["p2b_data_example"] <= html.P(str(data_arr))
doc["p2b_data_example"] <= html.P(str(type(data_arr)))

-h, --html option

In this example you can see how to create some HTML code in a cell and then use that HTML code in the brython cell. In this way you do not need to create the HTML code via scripting with Brython.

In [ ]:
html = """
<div id="paragraph">Hi</div>
"""
In [ ]:
%%brython -c html_ex -h html
from browser import doc

doc["paragraph"].style = {"color": "yellow",
                          "fontSize": "100px",
                          "lineHeight": "150px",
                          "textAlign": "center",
                          "backgroundColor": "black"}

-s, --script option

With this option you are creating a reference of the code in the Brython cell (e.g., an id of the HTML script tag created to run the Brython code). So, if you need to use the code of the Brython cell in a future Brython cell you could reference it by its id. Let's see this on an example (the -p option is used to show you the generated code and how the id of the script tag is created):

In [ ]:
%%brython -s my_dummy_function -p
def dummy_function(some_text):
    print(some_text)

-S, --scripts option

This option could be used to call code created in a previous Brython code cell using its id (see the -s option above). In the following code cell we will use the dummy_function created in another Brython code cell. The dummy_function was created in a script tag with an id="my_dummy_function".

[HINT] The result of the Brython code cell below is shown in the javascript console of your browser.

In [ ]:
%%brython -S my_dummy_function
dummy_function('Hi')

-f, --fiddle option

With this option, the code in the cell will be automatically uploaded to gist.github.com/ as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on jsfiddle.net. Finally, some links will be printed in the output linking to the gist and the fiddle.

In [ ]:
%%brython -f
from browser import alert
alert('hello world from jsfiddle!')

-e, --embedfiddle option

With this option, the code in the cell will be automatically uploaded to gist.github.com/ as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on jsfiddle.net. Finally, some links will be printed in the output linking to the gist and the fiddle and an iframe will be created showing the fiddle on jsfiddle.net.

In [ ]:
%%brython -e
from browser import alert
alert('hello world from jsfiddle!')

How to use Brython in the IPython notebook

First step should be to learn the brython documentation. You can find the docs here:

http://brython.info/doc/en/index.html?lang=en

In the following section I will show you some dummy examples.

Hello world example

In this example let's see how to pop up an alert window. This could be an standard 'Hello world!' example in the Brython world.

In [ ]:
%%brython
from browser import alert

alert('Hello world!, Welcome to the brythonmagic!')

Simple example, writing some numbers in the div container

In this example we just write inside a <div> ten numbers using a <P> tag for each number.

[HINT] To see the line numbers in the code cell just go to the cell and press <CTRL>-m and then l.

  • Line 2: We import the libraries to use
  • Line 4: A for loop :-P
  • Line 10: We create a P tag and write the value of i inside. Finally, add the P element to the selected div, in this case the div with "simple_example" id attribute.
In [ ]:
%%brython -c simple_example
from browser import doc, html

for i in range(10):
    doc["simple_example"] <= html.P(i)

A more useful example: A multiplication table

In the following cell we create a multiplication table. First, we create a table tag. We append the table rows and cells (TR and TD tags) and, finally, we append the final table to the div with "table" id attribute.

In [ ]:
%%brython -c table
from browser import doc, html

table = html.TABLE()

for i in range(10):
    color = ['cyan','#dddddd'] * 5
    table <= html.TR(
                     html.TD(str(i+1) + ' x 2 =', style = {'backgroundColor':color[i]}) + 
                     html.TD((i+1)*2, style = {'backgroundColor':color[i]}))
doc['table'] <= table

Let's add some animation using HTML5 canvas technology...

In the following example we draw a shape using the HTML5 canvas. Also, we add some controls to stop and animate the shape. The example has been adapted from the javascript example available here.

In [ ]:
%%brython -c canvas_example
from browser.timer import request_animation_frame as raf
from browser.timer import cancel_animation_frame as caf
from browser import doc, html
from time import time
import math

# First we create a table to insert the elements
table = html.TABLE(cellpadding = 10)
btn_anim = html.BUTTON('Animate', Id="btn-anim", type="button")
btn_stop = html.BUTTON('Stop', Id="btn-stop", type="button")
cnvs = html.CANVAS(Id="raf-canvas", width=256, height=256)

table <= html.TR(html.TD(btn_anim + btn_stop) +
                 html.TD(cnvs))

doc['canvas_example'] <= table
# Now we access the canvas context
ctx = doc['raf-canvas'].getContext( '2d' ) 

# And we create several functions in charge to animate and stop the draw animation
toggle = True

def draw():
    t = time() * 3
    x = math.sin(t) * 96 + 128
    y = math.cos(t * 0.9) * 96 + 128
    global toggle
    if toggle:
        toggle = False
    else:
        toggle = True
    ctx.fillStyle = 'rgb(200,200,20)' if toggle else 'rgb(20,20,200)'
    ctx.beginPath()
    ctx.arc( x, y, 6, 0, math.pi * 2, True)
    ctx.closePath()
    ctx.fill()

def animate(i):
    global id
    id = raf(animate)
    draw()

def stop(i):
    global id
    print(id)
    caf(id)

doc["btn-anim"].bind("click", animate)
doc["btn-stop"].bind("click", stop)

Interaction with other javascript libraries: D3.js

In Brython there is a javascript library that allows to access objects available in the javascript namespace. In this example we are using a javascript object (D3.js library) from Brython.

So, in order to allow Brython to access to D3 first you should load the D3 library.

In [ ]:
%%HTML
<script type="text/javascript" src="http://d3js.org/d3.v2.js"></script>

Now, we can access D3 objects using JSObject (see example below).

In [ ]:
%%brython -c simple_d3
from browser import window, document, html
from javascript import JSObject

d3 = window.d3

container = JSObject(d3.select("#simple_d3"))
svg = container.append("svg").attr("width", 100).attr("height", 100)
circle1 = svg.append("circle").style("stroke", "gray").style("fill", "gray").attr("r", 40)
circle1.attr("cx", 50).attr("cy", 50).attr("id", "mycircle")

circle2 = svg.append("circle").style("stroke", "gray").style("fill", "white").attr("r", 20)
circle2.attr("cx", 50).attr("cy", 50)

def over(ev):
    document["mycircle"].style.fill = "blue"

def out(ev):
    document["mycircle"].style.fill = "gray"

document["mycircle"].bind("mouseover", over)
document["mycircle"].bind("mouseout", out)

Manipulating the IPython notebook

An example to hide or show the code cells using a button.

In [ ]:
%%brython -c manipulating
from browser import document, html

def hide(ev):
    divs = document.get(selector = 'div.input')
    for div in divs:
        div.style.display = "none"

def show(ev):
    divs = document.get(selector = 'div.input')
    for div in divs:
        div.style.display = "inherit"

document["manipulating"] <= html.BUTTON('Hide code cells', Id="btn-hide")
document["btn-hide"].bind("click", hide)

document["manipulating"] <= html.BUTTON('Show code cells', Id="btn-show")
document["btn-show"].bind("click", show)

A more complete d3 example calculating things in Python and drawing results in Brython using D3.js

A more complete D3 example. In this case, first we create some data in Python.

In [ ]:
from random import randint

n = 100
x = [randint(0,800) for i in range(n)]
y = [randint(0,600) for i in range(n)]
r = [randint(25,50) for i in range(n)]
red = [randint(0,255) for i in range(n)]
green = [randint(0,255) for i in range(n)]
blue = [randint(0,255) for i in range(n)]

And now, the data is passed to Brython to be used in a D3 plot. In this case, the D3.js library is already loaded so it is not necessary to load it.

In [ ]:
%%brython -c other_d3 -i x y r red green blue
from browser import window, document, html
from javascript import JSObject

d3 = window.d3

WIDTH = 800
HEIGHT = 600

container = JSObject(d3.select("#other_d3"))
svg = container.append("svg").attr("width", WIDTH).attr("height", HEIGHT)

class AddShapes:
    def __init__(self, x, y, r, red, green, blue, shape = "circle", interactive = True):
        self.shape = shape
        self.interactive = interactive
        self._color = "gray"
        self.add(x, y, r, red, green, blue)

    def over(self, ev):
        self._color = ev.target.style.fill
        document[ev.target.id].style.fill = "white"
        
    def out(self, ev):
        document[ev.target.id].style.fill = self._color
    
    def add(self, x, y, r, red, green, blue):
        for i in range(len(x)):
            self.idx = self.shape + '_' + str(i) 
            self._color = "rgb(%s,%s,%s)" % (red[i], green[i], blue[i])
            shaped = svg.append(self.shape).style("stroke", "gray").style("fill", self._color).attr("r", r[i])
            shaped.attr("cx", x[i]).attr("cy", y[i]).attr("id", self.idx)
            if self.interactive:
                doc[self.idx].bind("mouseover", self.over)
                doc[self.idx].bind("mouseout", self.out)

plot = AddShapes(x, y, r, red, green, blue, interactive = True)

Mapping with Python in the IPython notebook using OpenLayers?

In the following example we will use OpenLayers to center a map in a specific location, with a zoom and a projection and then we will draw some vector points around the location.

As before, first we should load the OpenLayers.js library.

In [ ]:
%%HTML
<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>

And now we can create a map using JSConstructor available in the built-in Brython javascript library

In [ ]:
%%brython -c ol_map
from browser import document, window
from javascript import JSConstructor, JSObject

## Div layout
document['ol_map'].style.width = "800px"
document['ol_map'].style.height = "400px"
document['ol_map'].style.border = "1px solid black"

OpenLayers = window.OpenLayers

## Map
_map = JSConstructor(OpenLayers.Map)('ol_map')

## Addition of a OpenStreetMap layer
_layer = JSConstructor(OpenLayers.Layer.OSM)( 'Simple OSM map')
_map.addLayer(_layer)

## Map centered on Lon, Lat = (-3.671416, 40.435897) and a zoom = 14
## with a projection = "EPSG:4326" (Lat-Lon WGS84)
_proj = JSConstructor(OpenLayers.Projection)("EPSG:4326")
_center = JSConstructor(OpenLayers.LonLat)(-3.671416, 40.435897)
_center.transform(_proj, _map.getProjectionObject())
_map.setCenter(_center, 10)

## Addition of some points around the defined location
lons = [-3.670, -3.671, -3.672, -3.672, -3.672,
        -3.671, -3.670, -3.670]
lats = [40.435, 40.435, 40.435, 40.436, 40.437,
        40.437, 40.437, 40.436]

site_points = []
site_style = {}

points_layer = JSConstructor(OpenLayers.Layer.Vector)("Point Layer")
_map.addLayer(points_layer)

for lon, lat in zip(lons, lats):
    point = JSConstructor(OpenLayers.Geometry.Point)(lon, lat)
    point.transform(_proj, _map.getProjectionObject())
    _feat = JSConstructor(OpenLayers.Feature.Vector)(point)
    points_layer.addFeatures(_feat)

Using Raphaël.js

A dummy example using raphaƫl.js library.

As usual, first we should include the library:

In [ ]:
%%javascript
require([ "http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js" ], function (Raphael) {
    console.log(Raphael);
});

And now let's make a dumb example using JSObject.

In [ ]:
%%brython -c raphael_ex
from browser import window
from javascript import JSObject

Raphael = window.Raphael

paper = JSObject(Raphael("raphael_ex", 400, 400))

#Draw rectagle
rect = paper.rect(1,1,398,398)
rect.attr("stroke", "black")

#Draw orbits
for rot in range(90,280,60):
    ellipse = paper.ellipse(200, 200, 180, 50)
    ellipse.attr("stroke", "gray")
    ellipse.rotate(rot)

#Draw nucleus
nucleus = paper.circle(200,200,40)
nucleus.attr("fill", "black")

# Draw electrons
electron = paper.circle(200, 20, 10)
electron.attr("fill", "red")
electron = paper.circle(44, 290, 10)
electron.attr("fill", "yellow")
electron = paper.circle(356, 290, 10)
electron.attr("fill", "blue")

Include the cell number for each cell

The cells starts by 0 and all the cells (markdown, headings, code,...) has a number. If we want to re-run some cells in a programmatically way it is useful to know the number of the cells to identify them. You can delete the cell numbers using show_cell_number(on = False):

In [ ]:
%%brython
from browser import doc, html

def show_cell_number(on = True):
    cells = doc.get(selector = '.input_prompt')
    for i, cell in enumerate(cells):
        if on:
            if 'In' in cell.html and '<br>' not in cell.html:
                cell.html += "<br>cell #" + str(i)
        else:
            if 'In' in cell.text:
                cell.html = cell.html.split('<br>')[0]

show_cell_number(on = True)

Running Python cells as a loop

Imagine you have several cells of code and you want just to modify some data and run again these cells as a loop not having to create a big cell with the code of the cells together.

In [ ]:
%%brython
from javascript import JSObject
from browser import window

IPython = window.IPython
nb = IPython.notebook
# This is used to prevent an infinite loop
this_cell = nb.get_selected_index()

for i in range(1,10): # Ths will run cells 1 to 9 (the beginning of the nb)
    cell = nb.get_cell(i)
    if cell.cell_type == "code" and i != this_cell:
        cell.execute()

Get the code of all the cells and create a new cell with the code

If you want to compile all the code used in a notebook you can use this recipe (use crtl + Enter to run the cell if you don't want a bad behaviour):

In [ ]:
%%brython
from javascript import JSObject
from browser import window

IPython = window.IPython
nb = IPython.notebook
this_cell = nb.get_selected_index()
total_cells = nb.ncells()

code = ""
first_cell = True
for i in range(total_cells):
    cell = nb.get_cell(i)
    if cell.cell_type == "code" and i != this_cell:
        if first_cell:
            code += "# This cell has been generated automatically using a brython script\n\n"
            code += "# code from cell " + str(i) + '\n'
            first_cell = False
        else:
            code += "\n\n\n# code from cell " + str(i) + '\n'
        code += cell.get_text() + '\n'

nb.insert_cell_below('code')
new_cell = nb.get_cell(this_cell + 1)
new_cell.set_text(code)

Styling the nb

Lets modify a little bit the look of the notebook. Warning: The result will be very ugly...

In [ ]:
%%brython -s styling
from browser import doc, html

# Changing the background color
body = doc[html.BODY][0]
body.style = {"backgroundColor": "#99EEFF"}
    
# Changing the color of the imput prompt
inps = body.get(selector = ".input_prompt")
for inp in inps:
    inp.style = {"color": "blue"}
        
# Changin the color of the output cells
outs = body.get(selector = ".output_wrapper")
for out in outs:
    out.style = {"backgroundColor": "#E0E0E0"}
    
# Changing the font of the text cells
text_cells = body.get(selector = ".text_cell")
for cell in text_cells:
    cell.style = {"fontFamily": """"Courier New", Courier, monospace""",
                      "fontSize": "20px"}
        
# Changing the color of the code cells.
code_cells = body.get(selector = ".CodeMirror")
for cell in code_cells:
    cell.style = {"backgroundColor": "#D0D0D0"}